//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<layer.cpp>
///	@path	~/src/gui/composite/
///	@date	2008/05/25
///	@desc	.

#define	_FULLAPI
#include "config/config.h"

#include <CommCtrl.h>

#include "agg_blur.h"

#include "lib/utilities/observer.h"

#include "gui/composite/layer.h"

#pragma comment( linker , "/defaultlib:gdiPlus.lib" )

namespace xeres {

	namespace
	{
		/*!
			\class	_DrawShadowText
			\brief
		*/
		class _DrawShadowText
		{
		public:

			// instance
			__forceinline static _DrawShadowText& instance( void )
			{
				static _DrawShadowText s_DrawShadowText;
				return s_DrawShadowText;
			}

			/*!
				\brief		Proxy call.
			*/
			__forceinline int DrawShadowText( HDC hdc , LPCWSTR pszText , UINT cch , RECT *pRect ,
				DWORD dwFlags , COLORREF crText , COLORREF crShadow , int ixOffset , int iyOffset )
			{
				return DrawShadowTextFn( hdc , pszText , cch , pRect , dwFlags ,
					crText , crShadow , ixOffset , iyOffset );
			}

		private:

			//@ ctor
			_DrawShadowText( void )
			{
				static const wchar_t * COMCTL32_DLLNAME = _S("comctl32.dll");
				s_hDll = ::LoadLibrary( COMCTL32_DLLNAME );

				FARPROC Temp;
				Temp = ::GetProcAddress( s_hDll , "DrawShadowText" );
				if( Temp )
				{
					*(FARPROC*)&DrawShadowTextFn = Temp;
				}
				else
				{
					__win32_error( _S("_DrawShadowText::CTOR: Failed to get proc: '%s'.") );
				}
			}

			//@ dtor
			~_DrawShadowText( void )
			{
				if( s_hDll )
				{
					::FreeLibrary( s_hDll );
				}
			}

		//@ data
			HINSTANCE s_hDll;

			int (APIENTRY *DrawShadowTextFn)( HDC , LPCWSTR , UINT , RECT * ,
				DWORD , COLORREF , COLORREF , int , int );
		};

		static inline int DrawShadowText(
			HDC hdc , LPCWSTR pszText , UINT cch , RECT * prc , DWORD dwFlag , COLORREF crText , COLORREF crShadow ,
			int ixOffset , int iyOffset )
		{
			return _DrawShadowText::instance().DrawShadowText( hdc , pszText , cch , prc , dwFlag , crText , crShadow , ixOffset , iyOffset );
		}
	}

	// Layer
	Layer::Layer( void )
		: m_hdc(NULL)
		, m_hBmp(NULL)
		, m_bits(NULL)
		, m_hOldBmp(NULL)
		, m_width( 0 )
		, m_height( 0 )
	{
	}
	// ~Layer
	Layer::~Layer( void )
	{
		if( m_hdc )
		{
			::SelectObject( m_hdc , m_hOldBmp );
			::DeleteDC( m_hdc );
			::DeleteObject( m_hBmp );
			GetBitmap()->~Bitmap();
		}
	}
	// Resize
	void Layer::Resize( size_t width , size_t height )
	{
		XS_ASSERT( width > 0 && height > 0 );
		if( m_hdc )
		{
			if( width == m_width && height == m_height )
			{
				Clear();
				return;
			}
			::SelectObject( m_hdc , m_hOldBmp );
			::DeleteObject( m_hBmp );
			GetBitmap()->~Bitmap();
		}
		else
		{
			HDC dc = ::GetWindowDC( NULL );
			m_hdc = ::CreateCompatibleDC( dc );
			::ReleaseDC( NULL , dc );
		}
		m_width = (UINT)width;
		m_height = (UINT)height;
		memset( &m_bmi , 0 , sizeof(BITMAPINFO) );
        m_bmi.bmiHeader.biSize        = sizeof(BITMAPINFO);
        m_bmi.bmiHeader.biWidth       = (LONG)width;
        m_bmi.bmiHeader.biHeight      = (LONG)height;
        m_bmi.bmiHeader.biPlanes      = 1;
        m_bmi.bmiHeader.biBitCount    = 32;       // 32 bits per pixel.
        m_bmi.bmiHeader.biCompression = BI_RGB;
        m_bmi.bmiHeader.biSizeImage   = m_width * m_height * sizeof(ARGB); // 4 bytes per pixel
		m_hBmp = ::CreateDIBSection( NULL , &m_bmi , DIB_RGB_COLORS , (void**)&m_bits , NULL , 0 );
 		m_hOldBmp = (HBITMAP)::SelectObject( m_hdc , m_hBmp );
		::GdiFlush();
		// cleanup whole image
		Clear();
		// make bitmap
		::new (m_BitmapBuffer) Gdiplus::Bitmap( &m_bmi , m_bits );
	}
	// Clear
	void Layer::Clear( int left , int top , int width , int height )
	{
		// clipping
		if( left < 0 )
		{
			width -= -left;
			left = 0;
		}
		if( left >= (int)m_width ) return;
		if( top < 0 )
		{
			height -= -top;
			top = 0;
		}
		if( top >= (int)m_height ) return;
		if( width <= 0 ) return;
		if( ( left + width ) > (int)m_width ) width = (int)m_width - left;
		if( height <= 0 ) return;
		if( ( top + height ) > (int)m_height ) height = (int)m_height - top;
		//
		{
			ARGB * bits = m_bits;
			for( int y = top ; y < top + height ; ++y )
			{
				bits = m_bits + ( m_height - y - 1 ) * m_width + left;
				memset( bits , 0 , sizeof(ARGB) * width );
			}
		}
	}
	// Clear
	void Layer::Clear( void )
	{
		memset( m_bits , 0 , m_bmi.bmiHeader.biSizeImage );
	}
	// Fill
	void Layer::Fill( const PackColor4& packed ,int left , int top , int width , int height )
	{
		ARGB col = ARGB( packed.get_packed_argb() );
		// clipping
		if( left < 0 )
		{
			width -= -left;
			left = 0;
		}
		if( left >= (int)m_width ) return;
		if( top < 0 )
		{
			height -= -top;
			top = 0;
		}
		if( top >= (int)m_height ) return;
		if( width <= 0 ) return;
		if( ( left + width ) > (int)m_width ) width = (int)m_width - left;
		if( height <= 0 ) return;
		if( ( top + height ) > (int)m_height ) height = (int)m_height - top;
		//
		if( col.a == 255 )
		{
			ARGB * bits = m_bits;
			for( int y = top ; y < top + height ; ++y )
			{
				bits = m_bits + ( m_height - y - 1 ) * m_width + left;
				for( int x = 0 ; x < width ; ++x )
				{
					bits[x] = col;
				}
			}
		}
		else
		{
			uint RR = col.r; uint GG = col.g; uint BB = col.b; uint AA = col.a;

			ARGB * bits = m_bits;
			for( int y = top ; y < top + height ; ++y )
			{
				bits = m_bits + ( m_height - y - 1 ) * m_width + left;
				for( int x = 0 ; x < width ; ++x )
				{
					const uint sr = bits[x].r;
					const uint sg = bits[x].g;
					const uint sb = bits[x].b;
					const uint sa = bits[x].a;

					uint R = ( ( ( RR - sr ) * AA ) >> 8 ) + sr;
					uint G = ( ( ( GG - sg ) * AA ) >> 8 ) + sg;
					uint B = ( ( ( BB - sb ) * AA ) >> 8 ) + sb;
					uint A = ( sa + ( ( ( 255 - sa ) * AA ) >> 8 ) );

					uint * p = (uint*)bits + x;
					*p = ARGB::pack( A , R , G , B );
				}
			}
		}
	}

	// Draw
	void Layer::DrawText( const ARGB& ctext , HFONT fnt , const wchar_t * text , int len , const RECT& rc , const RECT& bound , UINT format )
	{
		if( len == -1 )
			len = (int)wcslen( text );
		if( len == 0 )
			return;

		if( ctext.a == 0 )
		{
			TRACE_WARNING( _S("Layer::DrawText: The color of text is transparent, no text will be drawn.") );
			return;
		}

		RECT rs;
		if( !IntersectRect( &rs , &rc , &bound ) )
			return;
		RECT rb;
		SetRect( &rb , 0 , 0 , m_width , m_height );
		if( !IntersectRect( &rs , &rs , &rb ) )
			return;

		int w = RectWidth( rs );
		int h = RectHeight( rs );

		int left = bound.left > rc.left ? rc.left - bound.left : 0;
		int top = bound.top > rc.top ? rc.top - bound.top : 0;

		int right = left + RectWidth(rc);
		int bottom = top + RectHeight(rc);

		byte * tb = NULL;
		byte * pb = NULL;

		BITMAPINFO bmi = {0};
		bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
		bmi.bmiHeader.biWidth       = w;
		bmi.bmiHeader.biHeight      = h;
		bmi.bmiHeader.biPlanes      = 1;
		bmi.bmiHeader.biBitCount    = 32; // 32 bits per pixel.
		bmi.bmiHeader.biCompression = BI_RGB;
		bmi.bmiHeader.biSizeImage   = bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * 4;

		// Make memory buffer.
		HDC ddc = ::GetWindowDC( NULL );
		HDC tdc = ::CreateCompatibleDC( ddc );

		HBITMAP textbmp = CreateDIBSection( tdc , &bmi , DIB_RGB_COLORS, (void**)&pb, NULL, 0 );
		HBITMAP old = (HBITMAP)::SelectObject( tdc , textbmp );
		RECT rf = { left , top , right , bottom };
		RECT rt = { left , top , right , bottom };

		// Settings.
		::SetTextColor( tdc , RGB(255,255,255) );
		::SetBkColor( tdc , 0 );
		::SetBkMode( tdc , OPAQUE );

		// Draw to buffer.
		HFONT oldFnt = (HFONT)::SelectObject( tdc , fnt );
		::DrawText( tdc , text , len , &rt , ( format & ~DT_CALCRECT ) | DT_TABSTOP | (4<<8) );

		LOGFONT lf;
		::GetObject( fnt , sizeof(LOGFONT) , &lf );

		// Draw text.
		if( lf.lfQuality == DEFAULT_QUALITY)
		{
			const uint _a = (uint)ctext.a;
			const uint _r = (uint)ctext.r;
			const uint _g = (uint)ctext.g;
			const uint _b = (uint)ctext.b;

			tb = pb;
			// for every pixel
			for( int y = 0 ; y < h ; ++y )
			{
				int py = y + rs.top;
				tb = ( pb + ( h - y - 1 ) * w * 4 );

				ARGB * pixel = GetPixelInvert( rs.left , py );

				for( int x = 0 ; x < w ; ++x , pixel++ , tb += 4 )
				{
					uint opaque = uint( tb[0] );
					if( opaque == 0 )
						continue;

					const uint sr = uint( pixel->r );
					const uint sg = uint( pixel->g );
					const uint sb = uint( pixel->b );
					const uint sa = uint( pixel->a );

					const uint R = ( ( ( _r - sr ) * _a >> 8 ) + sr );
					const uint G = ( ( ( _g - sg ) * _a >> 8 ) + sg );
					const uint B = ( ( ( _b - sb ) * _a >> 8 ) + sb );
					const uint A = ( sa + ( ( ( 255 - sa ) * _a ) >> 8 ) );

					uint * s = (uint*)pixel;
					*s = ((A)<<24) | ((R)<<16) | ((G)<<8) | (B);
				}
			}
		}
		else
		{ // Text pass.

			const uint _a = (uint)ctext.a;
			const uint _r = (uint)ctext.r;
			const uint _g = (uint)ctext.g;
			const uint _b = (uint)ctext.b;

			tb = pb;
			// for every pixel
			for( int y = 0 ; y < h ; ++y )
			{
				int py = y + rs.top;
				tb = ( pb + ( h - y - 1 ) * w * 4 );

				ARGB * pixel = GetPixelInvert( rs.left , py );

				for( int x = 0 ; x < w ; ++x , pixel++ , tb += 4 )
				{
					uint er = tb[2];
					uint eg = tb[1];
					uint eb = tb[0];

					uint sum = er + eg + eg + eb;

					if( sum == 0 )
						continue;

 					sum = sum >> 2;
					const uint sr = uint( pixel->r );
					const uint sg = uint( pixel->g );
					const uint sb = uint( pixel->b );
					const uint sa = uint( pixel->a );

					const uint a = ( sum * _a ) >> 8;

					const uint A = ( sa + ( ( ( 255 - sa ) * a ) >> 8 ) );
					const uint R = ( ( ( _r - sr ) * er >> 8 ) + sr );
					const uint G = ( ( ( _g - sg ) * eg >> 8 ) + sg );
					const uint B = ( ( ( _b - sb ) * eb >> 8 ) + sb );

					uint * s = (uint*)pixel;
					*s = ((A)<<24) | ((R)<<16) | ((G)<<8) | (B);
				}
			}
		}

		//
		::SelectObject( tdc , oldFnt );
		::SelectObject( tdc , old );
		::DeleteDC( tdc );
		::DeleteObject( textbmp );
		::ReleaseDC( NULL , ddc );
	}

	// DrawText
	void Layer::DrawText( const ARGB& ctext , RefWeak<StyleFont> font , const wchar_t * text , int len , const RECT& rc , const RECT& bound )
	{
		// draw fonts by font data

		if( len == -1 )
			len = (int)wcslen( text );
		if( len == 0 )
			return;

		if( ctext.a == 0 )
		{
			TRACE_WARNING( _S("Layer::DrawText: The color of text is transparent, no text will be drawn.") );
			return;
		}

		RECT rs;
		if( !IntersectRect( &rs , &rc , &bound ) )
			return;
		RECT rb;
		SetRect( &rb , 0 , 0 , m_width , m_height );
		if( !IntersectRect( &rs , &rs , &rb ) )
			return;

		int w = RectWidth( rs );
		int h = RectHeight( rs );

		int left = bound.left > rc.left ? rc.left - bound.left : 0;
		int top = bound.top > rc.top ? rc.top - bound.top : 0;

		int right = left + RectWidth(rc);
		int bottom = top + RectHeight(rc);

		int sx = left;
		int sy = top;
		int ds = rs.left;
		int dy = rs.top;

		int next = left;
		int height = font->GetFontHeight();

		// one by one
		for( int i = 0 ; i < len ; ++i )
		{
			wchar_t c = text[i];
			StyleFont::FontChar * ch = font->GetCharacter( c );

			sx = next;

			// font logical width
			int width = ch->m_step;

			// font sprite width
			int pitch = ch->m_width;

			next += width;

			if( sx + pitch < 0 )
				continue;
			if( sx >= right )
				break;

			if( pitch == 0 )
				continue;

			int ox = 0;
			int oy = 0;

			if( sx < 0 )
				ox = pitch + sx;

			int tw = min<int>( right - sx , pitch );
			int th = min<int>( bottom - sy , height );

			const uchar * src = ch->m_buffer;

			int ps = rs.left + sx;
			if( ps < 0 )
				ps = 0;

			const uint _a = (uint)ctext.a;
			const uint _r = (uint)ctext.r;
			const uint _g = (uint)ctext.g;
			const uint _b = (uint)ctext.b;

			for( int y = 0 ; y < th ; ++y )
			{
				int py = y + rs.top;
				ARGB * pixel = GetPixelInvert( ps , py );
				const ARGB * cc = (ARGB*)src + y * pitch;
				for( int x = 0 ; x < tw ; ++x , ++pixel , ++cc )
				{
					uint er = cc->r;
					uint eg = cc->g;
					uint eb = cc->b;
					uint ea = cc->a;
					const uint a = ( ea * _a ) >> 8;

					const uint sr = uint( pixel->r );
					const uint sg = uint( pixel->g );
					const uint sb = uint( pixel->b );
					const uint sa = uint( pixel->a );

					const uint A = ( sa + ( ( ( 255 - sa ) * a ) >> 8 ) );
					const uint R = ( ( ( _r - sr ) * er >> 8 ) + sr );
					const uint G = ( ( ( _g - sg ) * eg >> 8 ) + sg );
					const uint B = ( ( ( _b - sb ) * eb >> 8 ) + sb );

					uint * s = (uint*)pixel;
					*s = ((A)<<24) | ((R)<<16) | ((G)<<8) | (B);
				}
			}
		}
	}

	// DrawText
	void Layer::DrawText( const ARGB& ctext , RefWeak<StyleFont> font , const wchar_t * text , int len ,
		const RECT& bound , const Pair<int>& pos , int * arrangement , TextStyle * style , wchar_t prev )
	{
		if( len == -1 )
			len = (int)wcslen( text );
		if( len == 0 )
			return;

		if( ctext.a == 0 )
		{
			TRACE_WARNING( _S("Layer::DrawText: The color of text is transparent, no text will be drawn.") );
			return;
		}

		RECT rb;
		SetRect( &rb , 0 , 0 , m_width , m_height );

		RECT rs;
		if( !IntersectRect( &rs , &rb , &bound ) )
			return;

		Rect<int> clip( rs.left , rs.top , rs.right , rs.bottom );

		int fx = pos.x;
		int fy = pos.y;

		int height = font->GetFontHeight();

		wchar_t last = prev;
		// one by one
		for( int i = 0 ; i < len ; ++i )
		{
			wchar_t c = style ? style->GetLetter( text[i] , last ) : text[i];
			last = c;

			StyleFont::FontChar * ch = font->GetCharacter( c );

			// calculate font start point
			fx = pos.x + arrangement[i];

			// font logical width
			int width = ch->m_step;

			// font sprite width
			int pitch = ch->m_width;

			// boundary test
			if( fx + pitch < 0 )
				continue;
			if( fx >= rs.right )
				break;

			if( pitch == 0 )
				continue;

			Rect<int> fr = make_rect<int>( fx , fy , pitch , height );
			fr = fr & clip;
			if( fr.is_empty() )
				continue;

 			int ox = max<int>( 0 , fr.left() - fx );
			int oy = max<int>( 0 , fr.top() - fy );

			int fw = fr.width();
			int fh = fr.height();

			const uchar * src = ch->m_buffer;

			const uint _a = (uint)ctext.a;
			const uint _r = (uint)ctext.r;
			const uint _g = (uint)ctext.g;
			const uint _b = (uint)ctext.b;

			for( int y = 0 ; y < fh ; ++y )
			{
				ARGB * pixel = GetPixelInvert( fr.left() , fr.top() + y );
				const ARGB * cc = (ARGB*)src + ( oy + y ) * pitch + ox;
				for( int x = 0 ; x < fw ; ++x , ++pixel , ++cc )
				{
					uint er = cc->r;
					uint eg = cc->g;
					uint eb = cc->b;
					uint ea = cc->a;
					const uint a = ( ea * _a ) >> 8;

					const uint sr = uint( pixel->r );
					const uint sg = uint( pixel->g );
					const uint sb = uint( pixel->b );
					const uint sa = uint( pixel->a );

					const uint A = ( sa + ( ( ( 255 - sa ) * a ) >> 8 ) );
					const uint R = ( ( ( _r - sr ) * er >> 8 ) + sr );
					const uint G = ( ( ( _g - sg ) * eg >> 8 ) + sg );
					const uint B = ( ( ( _b - sb ) * eb >> 8 ) + sb );

					uint * s = (uint*)pixel;
					*s = ((A)<<24) | ((R)<<16) | ((G)<<8) | (B);
				}
			}
		}
	}

	// DrawShadowText
	void Layer::DrawShadowText( const ARGB& ctext , const ARGB& cshadow , HFONT fnt , const wchar_t * text , int len , const RECT& rc , const RECT& bound , UINT format , int cx , int cy )
	{
		if( len == -1 )
			len = (int)wcslen( text );
		if( len == 0 )
			return;

		if( ctext.a == 0 && cshadow.a == 0 )
		{
			TRACE_WARNING( _S("Layer::DrawShadowText: The color of text & shadow are both transparent, no text will be drawn.") );
			return;
		}

		RECT rs;
		if( !IntersectRect( &rs , &rc , &bound ) )
			return;
		RECT rb;
		SetRect( &rb , 0 , 0 , m_width , m_height );
		if( !IntersectRect( &rs , &rs , &rb ) )
			return;

		int w = RectWidth( rs );
		int h = RectHeight( rs );

		int left = bound.left > rc.left ? rc.left - bound.left : 0;
		int top = bound.top > rc.top ? rc.top - bound.top : 0;

		int right = left + RectWidth(rc);
		int bottom = top + RectHeight(rc);

		byte * tb = NULL;
		byte * pb = NULL;

		BITMAPINFO bmi = {0};
		bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
		bmi.bmiHeader.biWidth       = w;
		bmi.bmiHeader.biHeight      = h;
		bmi.bmiHeader.biPlanes      = 1;
		bmi.bmiHeader.biBitCount    = 32; // 32 bits per pixel.
		bmi.bmiHeader.biCompression = BI_RGB;
		bmi.bmiHeader.biSizeImage   = bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * 4; // 4 bytes per pixel

		HDC ddc = ::GetWindowDC( NULL );
		HDC tdc = ::CreateCompatibleDC( ddc );
		HBITMAP textbmp = CreateDIBSection( tdc , &bmi , DIB_RGB_COLORS, (void**)&pb, NULL, 0 );
		HBITMAP old = (HBITMAP)::SelectObject( tdc , textbmp );

		RECT rf = { left , top , right , bottom };
		RECT rt = { left /*+ cx*/ , top /*+ cy*/ , right /*- cx*/ , bottom /*- cy*/ };

		// erase
		::FillRect( tdc , &rf , (HBRUSH)GetStockObject(WHITE_BRUSH) );
		HFONT oldFnt = (HFONT)::SelectObject( tdc , fnt );
		int sheight = xeres::DrawShadowText( tdc , text , len , &rt , ( format & ~DT_CALCRECT ) | DT_TABSTOP | (4<<8) , RGB(255,255,255) , RGB(0,0,0) , cx , cy );

		//
		if( cshadow.a > 0 )
		{// shadow pass
			const int _a = (int)cshadow.a;
			const int _r = (int)cshadow.r;
			const int _g = (int)cshadow.g;
			const int _b = (int)cshadow.b;

			tb = pb;
			// for every pixel
			for( int y = 0 ; y < h ; ++y )
			{
				int py = y + rs.top;
				tb = ( pb + ( h - y - 1 ) * w * 4 );

				ARGB * pixel = GetPixelInvert( rs.left , py );

				for( int x = 0 ; x < w ; ++x , ++pixel , tb += 4 )
				{
					uint er = tb[2];
					uint eg = tb[1];
					uint eb = tb[0];

					uint sum = er + eg + eg + eb;

					if( sum == 0 )
						continue;

 					sum = sum >> 2;
					const uint sr = uint( pixel->r );
					const uint sg = uint( pixel->g );
					const uint sb = uint( pixel->b );
					const uint sa = uint( pixel->a );

					sum = 255 - sum;
					const uint a = ( sum * _a ) >> 8;

					const uint A = ( sa + ( ( ( 255 - sa ) * a ) >> 8 ) );
					const uint R = ( ( ( _r - sr ) * a >> 8 ) + sr );
					const uint G = ( ( ( _g - sg ) * a >> 8 ) + sg );
					const uint B = ( ( ( _b - sb ) * a >> 8 ) + sb );

					uint * s = (uint*)pixel;
					*s = ((A)<<24) | ((R)<<16) | ((G)<<8) | (B);
				}
			}
		}
		// text
		::FillRect( tdc , &rf , (HBRUSH)GetStockObject(BLACK_BRUSH) );
		// Settings.
		::SetTextColor( tdc , RGB(255,255,255) );
		::SetBkColor( tdc , 0 );
		::SetBkMode( tdc , OPAQUE );
		::DrawText( tdc , text , len , &rt , ( format & ~DT_CALCRECT ) | DT_TABSTOP | (4<<8) );

		//
		if( ctext.a > 0 )
		{ // text pass
			const int _a = (int)ctext.a;
			const int _r = (int)ctext.r;
			const int _g = (int)ctext.g;
			const int _b = (int)ctext.b;
			tb = pb;
			// for every pixel
			for( int y = 0 ; y < h ; ++y )
			{
				int py = y + rs.top;
				tb = ( pb + ( h - y - 1 ) * w * 4 );

				ARGB * pixel = GetPixelInvert( rs.left , py );

				for( int x = 0 ; x < w ; ++x , ++pixel , tb += 4 )
				{
					uint er = tb[2];
					uint eg = tb[1];
					uint eb = tb[0];

					uint sum = er + eg + eg + eb;

					if( sum == 0 )
						continue;

 					sum = sum >> 2;
					const uint sr = uint( pixel->r );
					const uint sg = uint( pixel->g );
					const uint sb = uint( pixel->b );
					const uint sa = uint( pixel->a );

					const uint a = ( sum * _a ) >> 8;

					const uint A = ( sa + ( ( ( 255 - sa ) * a ) >> 8 ) );
					const uint R = ( ( ( _r - sr ) * er >> 8 ) + sr );
					const uint G = ( ( ( _g - sg ) * eg >> 8 ) + sg );
					const uint B = ( ( ( _b - sb ) * eb >> 8 ) + sb );

					uint * s = (uint*)pixel;
					*s = ((A)<<24) | ((R)<<16) | ((G)<<8) | (B);
				}
			}
		}
		//
		::SelectObject( tdc , oldFnt );
		::SelectObject( tdc , old );
		::DeleteDC( tdc );
		::DeleteObject( textbmp );
		::ReleaseDC( NULL , ddc );
	}
	// GetTextWidth
	RECT Layer::GetTextWidth( HFONT fnt , const wchar_t * text , int len , UINT format , bool shadowed , int cx , int cy )
	{
		RECT result;
		SetRect( &result , 0 , 0 , 0 , 0 );

		HFONT oldFnt = (HFONT)::SelectObject( m_hdc , fnt );
		if( shadowed )
		{
			xeres::DrawShadowText( m_hdc , text , len , &result , ( format | DT_CALCRECT | DT_TABSTOP | (4<<8) ) , RGB(255,255,255) , RGB(0,0,0) , cx , cy );
		}
		else
		{
			::DrawText( m_hdc , text , len , &result , format | DT_CALCRECT | DT_TABSTOP | (4<<8) );
		}
		::SelectObject( m_hdc , oldFnt );
		return result;
	}
	namespace
	{
		
	}
	// Composite
	void Layer::Composite( const Pair<int>& start , const Rect<int>& bound , Layer * src_layer , const Rect<int>& region , int alpha )
	{
		const uint a = (uint)clamp( alpha , 0 , 255 );
		if( a == 0 )
			return;

		Rect<int> srcrect = region & make_rect( 0 , 0 , (int)src_layer->GetWidth() , (int)src_layer->GetHeight() );
		if( srcrect.is_empty() )
			return;
		srcrect.offset( start );
		Rect<int> rt = make_rect( 0 , 0 , (int)m_width , (int)m_height );
		Rect<int> sub = srcrect & rt;
		if( sub.is_empty() )
			return;
		Rect<int> rc = sub & bound;
		if( rc.is_empty() )
			return;

		int w = rc.width();
		int h = rc.height();

		int offset_x = srcrect.left();
		int offset_y = srcrect.top();

		if( a < 255 )
		{
			for( int y = 0 ; y < h ; ++y )
			{
				ARGB * src = src_layer->GetPixel( offset_x , offset_y + y );
				ARGB * dst = this->GetPixelInvert( start.x , start.y + y );

				for( int x = 0 ; x < w ; ++x , ++src , ++dst )
				{
					const uint sr = src->r;
					const uint sg = src->g;
					const uint sb = src->b;
					const uint sa = ( ( (uint)src->a) * a ) >> 8;

					const uint dr = dst->r;
					const uint dg = dst->g;
					const uint db = dst->b;
					const uint da = dst->a;

					const byte R = ( ( ( sr - dr ) * sa >> 8 ) + dr ) & 0xff;
					const byte G = ( ( ( sg - dg ) * sa >> 8 ) + dg ) & 0xff;
					const byte B = ( ( ( sb - db ) * sa >> 8 ) + db ) & 0xff;
					const byte A = ( da + ( ( ( 255 - da ) * sa ) >> 8 ) ) & 0xff;
					
					*dst = ARGB::pack( A , R , G , B );
				}
			}
		}
		else
		{
			for( int y = 0 ; y < h ; ++y )
			{
				ARGB * src = src_layer->GetPixel( offset_x , offset_y + y );
				ARGB * dst = this->GetPixelInvert( start.x , start.y + y );

				for( int x = 0 ; x < w ; ++x , ++src , ++dst )
				{
					const uint sr = src->r;
					const uint sg = src->g;
					const uint sb = src->b;
					const uint sa = src->a;

					const uint dr = dst->r;
					const uint dg = dst->g;
					const uint db = dst->b;
					const uint da = dst->a;

					const byte R = ( ( ( sr - dr ) * sa >> 8 ) + dr ) & 0xff;
					const byte G = ( ( ( sg - dg ) * sa >> 8 ) + dg ) & 0xff;
					const byte B = ( ( ( sb - db ) * sa >> 8 ) + db ) & 0xff;
					const byte A = ( da + ( ( ( 255 - da ) * sa ) >> 8 ) ) & 0xff;
					
					*dst = ARGB::pack( A , R , G , B );
				}
			}
		}
	}
	
	static void _composite( IBitmap::Sampler& sampler , Layer * layer , const Pair<int>& start , int w , int h , int a )
	{
		switch( sampler.m_type )
		{
		case COLOR_MONO:
			if( a < 255 )
			{
				for( int y = 0 ; y < h ; ++y )
				{
					const byte * src = sampler.m_bits + sampler.m_pitch * y;
					ARGB * dst = layer->GetPixelInvert( start.x , start.y + y );

					for( int x = 0 ; x < w ; ++x , ++src , ++dst )
					{
						*dst = Blend<ARGB>::solve( (uint)*src , a , *dst );
					}
				}
			}
			else
			{
				for( int y = 0 ; y < h ; ++y )
				{
					const byte * src = sampler.m_bits + sampler.m_pitch * y;
					ARGB * dst = layer->GetPixelInvert( start.x , start.y + y );

					for( int x = 0 ; x < w ; ++x , ++src , ++dst )
					{
						*dst = ARGB::pack( 255 , src[0] , src[0] , src[0] );
					}
				}
			}
			break;

		case COLOR_RGB:

			if( a < 255 )
			{
				for( int y = 0 ; y < h ; ++y )
				{
					const byte * src = sampler.m_bits + sampler.m_pitch * y;
					ARGB * dst = layer->GetPixelInvert( start.x , start.y + y );

					for( int x = 0 ; x < w ; ++x , src+=3 , ++dst )
					{
						*dst = Blend<ARGB>::solve( src[2] , src[1] , src[0] , a , *dst );
					}
				}
			}
			else
			{
				for( int y = 0 ; y < h ; ++y )
				{
					const byte * src = sampler.m_bits + sampler.m_pitch * y;
					ARGB * dst = layer->GetPixelInvert( start.x , start.y + y );

					for( int x = 0 ; x < w ; ++x , src+=3 , ++dst )
					{
						*dst = ARGB::pack( 255 , src[2] , src[1] , src[0] );
					}
				}
			}
			break;

		case COLOR_RGBA:
			if( a < 255 )
			{
				for( int y = 0 ; y < h ; ++y )
				{
					const RGBA * src = (const RGBA*)( sampler.m_bits + sampler.m_pitch * y );
					ARGB * dst = layer->GetPixelInvert( start.x , start.y + y );

					for( int x = 0 ; x < w ; ++x , ++src , ++dst )
					{
						*dst = Blend<ARGB>::solve( *src , a , *dst );
					}
				}
			}
			else
			{
				for( int y = 0 ; y < h ; ++y )
				{
					const RGBA * src = (const RGBA*)( sampler.m_bits + sampler.m_pitch * y );
					ARGB * dst = layer->GetPixelInvert( start.x , start.y + y );

					for( int x = 0 ; x < w ; ++x , ++src , ++dst )
					{	
						*dst = Blend<ARGB>::solve( src->r , src->g , src->b , src->a , *dst );
					}
				}
			}
			break;
		case COLOR_ARGB:
			if( a < 255 )
			{
				for( int y = 0 ; y < h ; ++y )
				{
					const ARGB * src = (const ARGB*)( sampler.m_bits + sampler.m_pitch * y );
					ARGB * dst = layer->GetPixelInvert( start.x , start.y + y );

					for( int x = 0 ; x < w ; ++x , ++src , ++dst )
					{
						*dst = Blend<ARGB>::solve( *src , a , *dst );
					}
				}
			}
			else
			{
				for( int y = 0 ; y < h ; ++y )
				{
					const ARGB * src = (const ARGB*)( sampler.m_bits + sampler.m_pitch * y );
					ARGB * dst = layer->GetPixelInvert( start.x , start.y + y );

					for( int x = 0 ; x < w ; ++x , ++src , ++dst )
					{
						*dst = Blend<ARGB>::solve( *src , *dst );
					}
				}
			}
			break;
		}
	}

	// Composite
	void Layer::Composite( const Pair<int>& start , const Rect<int>& bound , RefWeak<IBitmap> bitmap , const Rect<int>& region , int alpha , int frame )
	{
		const uint a = (uint)clamp( alpha , 0 , 255 );
		if( a == 0 )
		{
			return;
		}

		Pair<int> pos( start );

		Pair<int> cut(0);
		if( start.x < bound.left() )
			cut.x = bound.left() - start.x;
		if( start.y < bound.top() )
			cut.y = bound.top() - start.y;

		pos += cut;

		Rect<int> srcrect = region;
		srcrect.offset( cut );
		srcrect = srcrect & region;
		if( srcrect.is_empty() )
			return;
		srcrect = srcrect & Rect<int>( 0 , 0 , bitmap->GetSize() );
		if( srcrect.is_empty() )
			return;

		Pair<int> offset( srcrect.left() , srcrect.top() );
		Rect<int> dstrect = make_rect( pos.x , pos.y , srcrect.width() , srcrect.height() );

		Rect<int> rt = make_rect( 0 , 0 , (int)m_width , (int)m_height );
		Rect<int> sub = dstrect & rt;
		if( sub.is_empty() )
			return;
		Rect<int> rc = sub & bound;
		if( rc.is_empty() )
			return;

		int w = rc.width();
		int h = rc.height();

		if( start.x < 0 )
		{
			pos.x = 0;
			offset.x -= start.x;
		}
		if( start.y < 0 )
		{
			pos.y = 0;
			offset.y -= start.y;
		}

		if( offset.x >= (int)bitmap->GetWidth() || offset.y >= (int)bitmap->GetHeight() )
			return;

		IBitmap::Sampler sampler;
		bitmap->GetSampler( Pair<int>(offset.x,offset.y) , sampler , frame );

		_composite( sampler , this , pos , w , h , a );
	}
	// BlendFrame
	void Layer::BlendFrame( const Pair<int>& start , const Rect<int>& bound , RefWeak<IBitmap> bitmap , const Rect<int>& region , int alpha , int frame1 , int frame2 , float blending )
	{
		const uint a = (uint)clamp( alpha , 0 , 255 );
		if( a == 0 )
		{
			return;
		}

		const uint b = (uint)clamp( blending * 255.f , 0.f , 255.f );
		if( b == 0 )
		{
			Composite( start , bound , bitmap , region , alpha , frame1 );
		}
		else if( b == 255 )
		{
			Composite( start , bound , bitmap , region , alpha , frame2 );
		}

		const uint b1 = ( a * ( 255 - b ) ) >> 8;
		const uint b2 = ( a * b ) >> 8;
		Composite( start , bound , bitmap , region , b1 , frame1 );
		Composite( start , bound , bitmap , region , b2 , frame2 );
	}
	namespace
	{
		using namespace agg;

		enum color_order
		{ 
			A = 0,
			R = 1, 
			G = 2, 
			B = 3
		};

		void stack_blur_rgba32( Layer& img, unsigned rx, unsigned ry , unsigned left , unsigned top , unsigned width , unsigned height )
		{
			unsigned x, y, xp, yp, i;
			unsigned stack_ptr;
			unsigned stack_start;

			const uchar * src_pix_ptr;
			uchar * dst_pix_ptr;
			ARGB *  stack_pix_ptr;

			unsigned sum_r;
			unsigned sum_g;
			unsigned sum_b;
			unsigned sum_a;
			unsigned sum_in_r;
			unsigned sum_in_g;
			unsigned sum_in_b;
			unsigned sum_in_a;
			unsigned sum_out_r;
			unsigned sum_out_g;
			unsigned sum_out_b;
			unsigned sum_out_a;

			unsigned w   = width;
			unsigned h   = height;
			unsigned wm  = w - 1;
			unsigned hm  = h - 1;

			unsigned div;
			unsigned mul_sum;
			unsigned shr_sum;

			if(rx > 0)
			{
				if(rx > 254) rx = 254;
				div = rx * 2 + 1;
				mul_sum = stack_blur_tables<int>::g_stack_blur8_mul[rx];
				shr_sum = stack_blur_tables<int>::g_stack_blur8_shr[rx];
				ARGB * stack = (ARGB*)alloca( div * sizeof(ARGB) ); //.allocate(div);

				for(y = 0; y < h; y++)
				{
					sum_r = 
						sum_g = 
						sum_b = 
						sum_a = 
						sum_in_r = 
						sum_in_g = 
						sum_in_b = 
						sum_in_a = 
						sum_out_r = 
						sum_out_g = 
						sum_out_b = 
						sum_out_a = 0;

					src_pix_ptr = (int8u*)img.GetPixelInvert( left , y + top );
					for(i = 0; i <= rx; i++)
					{
						stack_pix_ptr    = &stack[i];
						stack_pix_ptr->r = src_pix_ptr[R];
						stack_pix_ptr->g = src_pix_ptr[G];
						stack_pix_ptr->b = src_pix_ptr[B];
						stack_pix_ptr->a = src_pix_ptr[A];
						sum_r           += src_pix_ptr[R] * (i + 1);
						sum_g           += src_pix_ptr[G] * (i + 1);
						sum_b           += src_pix_ptr[B] * (i + 1);
						sum_a           += src_pix_ptr[A] * (i + 1);
						sum_out_r       += src_pix_ptr[R];
						sum_out_g       += src_pix_ptr[G];
						sum_out_b       += src_pix_ptr[B];
						sum_out_a       += src_pix_ptr[A];
					}
					for(i = 1; i <= rx; i++)
					{
						if(i <= wm) src_pix_ptr += 4;
						stack_pix_ptr = &stack[i + rx];
						stack_pix_ptr->r = src_pix_ptr[R];
						stack_pix_ptr->g = src_pix_ptr[G];
						stack_pix_ptr->b = src_pix_ptr[B];
						stack_pix_ptr->a = src_pix_ptr[A];
						sum_r           += src_pix_ptr[R] * (rx + 1 - i);
						sum_g           += src_pix_ptr[G] * (rx + 1 - i);
						sum_b           += src_pix_ptr[B] * (rx + 1 - i);
						sum_a           += src_pix_ptr[A] * (rx + 1 - i);
						sum_in_r        += src_pix_ptr[R];
						sum_in_g        += src_pix_ptr[G];
						sum_in_b        += src_pix_ptr[B];
						sum_in_a        += src_pix_ptr[A];
					}

					stack_ptr = rx;
					xp = rx;
					if(xp > wm) xp = wm;
					src_pix_ptr = (int8u*)img.GetPixelInvert( left + xp , top + y );
					dst_pix_ptr = (int8u*)img.GetPixelInvert( left , top + y );
					for(x = 0; x < w; x++)
					{
						dst_pix_ptr[R] = (sum_r * mul_sum) >> shr_sum;
						dst_pix_ptr[G] = (sum_g * mul_sum) >> shr_sum;
						dst_pix_ptr[B] = (sum_b * mul_sum) >> shr_sum;
						dst_pix_ptr[A] = (sum_a * mul_sum) >> shr_sum;
						dst_pix_ptr += 4;

						sum_r -= sum_out_r;
						sum_g -= sum_out_g;
						sum_b -= sum_out_b;
						sum_a -= sum_out_a;

						stack_start = stack_ptr + div - rx;
						if(stack_start >= div) stack_start -= div;
						stack_pix_ptr = &stack[stack_start];

						sum_out_r -= stack_pix_ptr->r;
						sum_out_g -= stack_pix_ptr->g;
						sum_out_b -= stack_pix_ptr->b;
						sum_out_a -= stack_pix_ptr->a;

						if(xp < wm) 
						{
							src_pix_ptr += 4;
							++xp;
						}

						stack_pix_ptr->r = src_pix_ptr[R];
						stack_pix_ptr->g = src_pix_ptr[G];
						stack_pix_ptr->b = src_pix_ptr[B];
						stack_pix_ptr->a = src_pix_ptr[A];

						sum_in_r += src_pix_ptr[R];
						sum_in_g += src_pix_ptr[G];
						sum_in_b += src_pix_ptr[B];
						sum_in_a += src_pix_ptr[A];
						sum_r    += sum_in_r;
						sum_g    += sum_in_g;
						sum_b    += sum_in_b;
						sum_a    += sum_in_a;

						++stack_ptr;
						if(stack_ptr >= div) stack_ptr = 0;
						stack_pix_ptr = &stack[stack_ptr];

						sum_out_r += stack_pix_ptr->r;
						sum_out_g += stack_pix_ptr->g;
						sum_out_b += stack_pix_ptr->b;
						sum_out_a += stack_pix_ptr->a;
						sum_in_r  -= stack_pix_ptr->r;
						sum_in_g  -= stack_pix_ptr->g;
						sum_in_b  -= stack_pix_ptr->b;
						sum_in_a  -= stack_pix_ptr->a;
					}
				}
			}

			if(ry > 0)
			{
				if(ry > 254) ry = 254;
				div = ry * 2 + 1;
				mul_sum = stack_blur_tables<int>::g_stack_blur8_mul[ry];
				shr_sum = stack_blur_tables<int>::g_stack_blur8_shr[ry];
				ARGB * stack = (ARGB*)alloca( div * sizeof(ARGB) );

				int stride = -(int)(img.GetWidth() * sizeof(ARGB));
				for(x = 0; x < w; x++)
				{
					sum_r = 
						sum_g = 
						sum_b = 
						sum_a = 
						sum_in_r = 
						sum_in_g = 
						sum_in_b = 
						sum_in_a = 
						sum_out_r = 
						sum_out_g = 
						sum_out_b = 
						sum_out_a = 0;

					src_pix_ptr = (int8u*)img.GetPixelInvert( left + x , top );
					for(i = 0; i <= ry; i++)
					{
						stack_pix_ptr    = &stack[i];
						stack_pix_ptr->r = src_pix_ptr[R];
						stack_pix_ptr->g = src_pix_ptr[G];
						stack_pix_ptr->b = src_pix_ptr[B];
						stack_pix_ptr->a = src_pix_ptr[A];
						sum_r           += src_pix_ptr[R] * (i + 1);
						sum_g           += src_pix_ptr[G] * (i + 1);
						sum_b           += src_pix_ptr[B] * (i + 1);
						sum_a           += src_pix_ptr[A] * (i + 1);
						sum_out_r       += src_pix_ptr[R];
						sum_out_g       += src_pix_ptr[G];
						sum_out_b       += src_pix_ptr[B];
						sum_out_a       += src_pix_ptr[A];
					}
					for(i = 1; i <= ry; i++)
					{
						if(i <= hm) src_pix_ptr += stride; 
						stack_pix_ptr = &stack[i + ry];
						stack_pix_ptr->r = src_pix_ptr[R];
						stack_pix_ptr->g = src_pix_ptr[G];
						stack_pix_ptr->b = src_pix_ptr[B];
						stack_pix_ptr->a = src_pix_ptr[A];
						sum_r           += src_pix_ptr[R] * (ry + 1 - i);
						sum_g           += src_pix_ptr[G] * (ry + 1 - i);
						sum_b           += src_pix_ptr[B] * (ry + 1 - i);
						sum_a           += src_pix_ptr[A] * (ry + 1 - i);
						sum_in_r        += src_pix_ptr[R];
						sum_in_g        += src_pix_ptr[G];
						sum_in_b        += src_pix_ptr[B];
						sum_in_a        += src_pix_ptr[A];
					}

					stack_ptr = ry;
					yp = ry;
					if(yp > hm) yp = hm;
					src_pix_ptr = (int8u*)img.GetPixelInvert( left + x , top + yp );
					dst_pix_ptr = (int8u*)img.GetPixelInvert( left + x , top + 0 );
					for(y = 0; y < h; y++)
					{
						dst_pix_ptr[R] = (sum_r * mul_sum) >> shr_sum;
						dst_pix_ptr[G] = (sum_g * mul_sum) >> shr_sum;
						dst_pix_ptr[B] = (sum_b * mul_sum) >> shr_sum;
						dst_pix_ptr[A] = (sum_a * mul_sum) >> shr_sum;
						dst_pix_ptr += stride;

						sum_r -= sum_out_r;
						sum_g -= sum_out_g;
						sum_b -= sum_out_b;
						sum_a -= sum_out_a;

						stack_start = stack_ptr + div - ry;
						if(stack_start >= div) stack_start -= div;

						stack_pix_ptr = &stack[stack_start];
						sum_out_r -= stack_pix_ptr->r;
						sum_out_g -= stack_pix_ptr->g;
						sum_out_b -= stack_pix_ptr->b;
						sum_out_a -= stack_pix_ptr->a;

						if(yp < hm) 
						{
							src_pix_ptr += stride;
							++yp;
						}

						stack_pix_ptr->r = src_pix_ptr[R];
						stack_pix_ptr->g = src_pix_ptr[G];
						stack_pix_ptr->b = src_pix_ptr[B];
						stack_pix_ptr->a = src_pix_ptr[A];

						sum_in_r += src_pix_ptr[R];
						sum_in_g += src_pix_ptr[G];
						sum_in_b += src_pix_ptr[B];
						sum_in_a += src_pix_ptr[A];
						sum_r    += sum_in_r;
						sum_g    += sum_in_g;
						sum_b    += sum_in_b;
						sum_a    += sum_in_a;

						++stack_ptr;
						if(stack_ptr >= div) stack_ptr = 0;
						stack_pix_ptr = &stack[stack_ptr];

						sum_out_r += stack_pix_ptr->r;
						sum_out_g += stack_pix_ptr->g;
						sum_out_b += stack_pix_ptr->b;
						sum_out_a += stack_pix_ptr->a;
						sum_in_r  -= stack_pix_ptr->r;
						sum_in_g  -= stack_pix_ptr->g;
						sum_in_b  -= stack_pix_ptr->b;
						sum_in_a  -= stack_pix_ptr->a;
					}
				}
			}
		}

		// down sample image
		void down_sample( Layer& layer , const Rect<int>& region , int radius )
		{
			const int radius2 = radius * radius;
			const int stride = -(int)layer.GetWidth();
			const int sx = region.left();
			const int sy = region.top();
			int w = region.width();
			int h = region.height();
			int tw = w % radius;
			int th = h % radius;
			const int tt = tw * th;
			const int twr = tw * radius;
			const int thr = th * radius;
			w -= tw;
			h -= th;
			for( int y = 0 ; y < h  - radius ; y += radius )
			{
				ARGB * c = layer.GetPixelInvert( sx , sy + y );
				for( int x = 0 ; x < w - radius ; x += radius , c += radius )
				{
					int r = 0 , g = 0 , b = 0 , a = 0;
					for( int i = 0 ; i < radius ; ++i )
					{
						for( int j = 0 ; j < radius ; ++j )
						{
							r += c[stride*i+j].r;
							g += c[stride*i+j].g;
							b += c[stride*i+j].b;
							a += c[stride*i+j].a;
						}
					}
					r /= radius2; g /= radius2; b /= radius2; a /= radius2;
					for( int i = 0 ; i < radius ; ++i )
					{
						for( int j = 0 ; j < radius ; ++j )
						{
							c[stride*i+j].r = r;
							c[stride*i+j].g = g;
							c[stride*i+j].b = b;
							c[stride*i+j].a = a;
						}
					}
				}

				if( tw == 0 )
					continue;

				for( int x = w ; x < w + tw ; x += tw , c += tw )
				{
					int r = 0 , g = 0 , b = 0 , a = 0;
					for( int i = 0 ; i < radius ; ++i )
					{
						for( int j = 0 ; j < tw ; ++j )
						{
							r += c[stride*i+j].r;
							g += c[stride*i+j].g;
							b += c[stride*i+j].b;
							a += c[stride*i+j].a;
						}
					}
					r /= twr; g /= twr; b /= twr; a /= twr;
					for( int i = 0 ; i < radius ; ++i )
					{
						for( int j = 0 ; j < tw ; ++j )
						{
							c[stride*i+j].r = r;
							c[stride*i+j].g = g;
							c[stride*i+j].b = b;
							c[stride*i+j].a = a;
						}
					}
				}
			}
		}
	}
	// Blur
	void Layer::Blur( const Rect<int>& rect , int radius )
	{
		if( rect.width() <= 0 || rect.height() <= 0 || rect.is_empty() )
			return;
		Rect<int> rr( make_rect( 0 , 0 , (int)m_width , (int)m_height ) );
		rr = rr & rect;
		stack_blur_rgba32( *this , radius , radius , rr.left() , rr.top() , rr.width() , rr.height() );
	}

	// DownSample
	void Layer::DownSample( const Rect<int>& rect , int radius )
	{
		if( rect.width() <= 0 || rect.height() <= 0 || rect.is_empty() )
			return;
		Rect<int> rr( make_rect( 0 , 0 , (int)m_width , (int)m_height ) );
		rr = rr & rect;
		down_sample( *this , rect , radius );
	}
	
} // namespace xeres
